import copy

class Dictionary:
        def __init__(self, dictionaryFilepath):
                import csv
                self.dictReader = csv.reader(open(dictionaryFilepath, 'rb'))
                self.itemArray = {}
                row = self.dictReader.next()
                while row:
                    ingType = row.pop(0)
                    self.itemArray[ingType] = []
                    for item in row:
                        self.itemArray[ingType].append(Ingredient(item, ingType, 'NA', 'NA'))
                    try:
                        row = self.dictReader.next()
                    except:
                        break
        def __str__(self):
            output = ""
            for ingType, ingArray in self.itemArray.iteritems():
                output += ingType.title() + ": "
                for ing in ingArray:
                    output += str(ing) + ", "
                output = output.rstrip(', ')
                output += '\n'
            return output

        def getIngredients(self):
            output = []
            for ingType, ingArray in self.itemArray.iteritems():
                if ingType not in ('Equipment','Product','Prep Action','Cook Action','Unit'):
                   for ing in ingArray:
                        output.append(ing)
            return output

        def getActions(self):
            output = []
            for ingType, ingArray in self.itemArray.iteritems():
                if ingType in ('Action'):
                    for ing in ingArray:
                        output.append(ing)
            return output

        def getEquipment(self):
            output = []
            for ingType, ingArray in self.itemArray.iteritems():
                if ingType in ('Equipment'):
                    for ing in ingArray:
                        output.append(ing)
            return output
                                                
class Ingredient:
        def __init__(self, name, ingType, amount=0, unit='NA'):
                self.name = name.title()
                self.ingType = ingType
                self.amount = amount
                self.unit = unit
        def __str__(self):
                return self.name
        def printIng(self):
                return str(self.amount) + " " + self.unit + " " + self.name

class Step:
        def __init__(self, originalString, modified, ingredientArray, action, actionParameter, target, equipmentArray, product):
                self.ingredientArray = ingredientArray
                self.action = action
                self.actionParameter = actionParameter
                self.target = target.title()
                self.equipmentArray = equipmentArray
                self.originalString = originalString
                self.modified = modified
                self.product = product
        
        def __str__(self):
                """if self.modified:
                        desc = self.action.title()  + " "
                        for singleIng in self.ingredientArray:
                                if singleIng.amount>0 and singleIng.name != self.target:
                                        desc += str(singleIng.amount) + " "+ str(singleIng.unit) + " "
                                desc += str(singleIng).lower() + ", "
                        desc = desc.rstrip(", ")
                        if self.actionParameter != 'NA':
                                desc += " for " + str(self.actionParameter)
                        desc +=  " with target " + self.target.lower() + ".<br>"
                        return desc
                else:"""
                return self.originalString + '<br>'

class Recipe: 
        def __init__(self, title, ingredientArray, timeToCook, yieldAmount, stepArray):
                self.title = title.title()
                self.ingredientArray = ingredientArray
                self.timeToCook = timeToCook
                self.yieldAmount = yieldAmount
                self.stepArray = stepArray

        def getSteps(self):
                output = ""
                for singleStep in self.stepArray:
                        output += str(singleStep) + "\n"
                return output

        def __str__(self):
                output = "Title: "
                output += self.title + "<br>"
                output += "Time: " + str(self.timeToCook) + " minutes<br>"
                output += "Yields: " +str(self.yieldAmount) + " servings<br>"
                output += "Ingredients : "
                for ing in self.ingredientArray:
                        output += ing.printIng() + "<br>"
                output = output.rstrip(', ')
                output += "<br>"
                output += "Steps: "
                output += self.getSteps()
                return output
        

        def swapMainIngredient(self, newMain):
                mainIngredientAmount = -1;
                for ingrd in self.ingredientArray:
                    if (normalizeQuantity(ingrd) > mainIngredientAmount):
                        mainIngredient = ingrd
                        mainIngredientAmount = normalizeQuantity(ingrd)
                newMainIngredient = Ingredient(newMain,mainIngredient.ingType, mainIngredient.amount, mainIngredient.unit)
                return self.swapIngredient(mainIngredient, newMainIngredient)
            
        def swapSpiceProfile(self, newSpiceProfile, dictionary):
                oldSpiceProfile = self.getSpiceProfile()
                if oldSpiceProfile == -1:
                    return -1
                newSpiceOptions = dictionary.itemArray[newSpiceProfile.title()]
                for ingrd in self.ingredientArray:
                    if(ingrd.ingType == oldSpiceProfile):
                        try:
                            newSpice = newSpiceOptions.pop()
                        except:
                            return -1
                        self.swapIngredient(ingrd, newSpice)
                return 0

        def getSpiceProfile(self):
                spiceProfiles = {}
                maxCount = 0
                outputProfile = -1
                for ing in self.ingredientArray:
                    if ing.ingType in spiceProfiles:
                        spiceProfiles[ing.ingType] += 1
                    else: 
                        spiceProfiles[ing.ingType] = 1
                for ingType, count in spiceProfiles.iteritems():
                    if count > maxCount and 'spice' in ingType.lower() :
                        maxCount = count
                        outputProfile = ingType      
                return outputProfile
 
        def getIngredientsByType(self, ingType):
                resultArray = []
                for ing in self.ingredientArray:
                    if ing.ingType == ingType:
                        resultArray.append(ing)
                return resultArray

                                        
        def swapIngredient(self, oldIngredient, newIngredient):
                oldIngredientCopy = copy.copy(oldIngredient) 
                try:
                    newIngredient.amount = oldIngredientCopy.amount
                except:
                    return -1
                self.title = self.title.replace(oldIngredientCopy.name, newIngredient.name)
                self.title = self.title.title()
                newIngredient.unit = oldIngredientCopy.unit
                self.ingredientArray.remove(oldIngredient)
                self.ingredientArray.append(newIngredient)
                for step in self.stepArray:
                    if step.target == oldIngredientCopy.name:
                        step.target = newIngredient.name
                        step.modified = True
                    for stepIng in step.ingredientArray :
                        if stepIng.name ==  oldIngredientCopy.name:
                            stepIng.name = newIngredient.name
                            step.modified = True
                    if step.modified == True:
                        step.originalString = step.originalString.replace(oldIngredientCopy.name.lower(), newIngredient.name.lower())
                return self
           
               
        def makeVegetarian(self):
                ingrdType = 'protein'
                mainIngredientAmount = -1
                for ingrd in self.ingredientArray:
                        if (ingrd.ingType.lower() == ingrdType 
                        and normalizeQuantity(ingrd) > mainIngredientAmount):
                                mainIngredient = ingrd
                                mainIngredientAmount = normalizeQuantity(ingrd)
                VegIng = Ingredient('tofu', 'protein')
                return self.swapIngredient(mainIngredient, VegIng)
                
                
        def getActionName(self, actionType, dictionary):
                actionName = []
                for actionIng in dictionary.itemArray[actionType]:
                        actionName.append(actionIng.name.title())
                return actionName
        
        def swapCookingMethod(self, newCookingMethod, dictionary):
                cookingAction = self.getActionName('Cook Action', dictionary)
                for step in self.stepArray:
                        isCookingStep = False
                        for action in cookingAction:
                            if action.lower() in step.originalString or action.title() in step.originalString:
                                isCookingStep = True
                                oldCookingMethod = action
                                break
                        if isCookingStep:
                                step.action = oldCookingMethod
                       # if step.action.title() in cookingAction and step.action.title() != newCookingMethod.title():
                                self.swapCookingMethodPerStep(step, newCookingMethod)
                return self
                                
        def swapCookingMethodPerStep(self, oldStep, newCookingMethod):
                if oldStep not in self.stepArray:
                        desc = "The old step is not in the recipe"
                        return desc
                oldStepIndex = self.stepArray.index(oldStep)
                
                # change action and action parameter
                newStep = copy.copy(oldStep)
                newStep.action = newCookingMethod.title()
                #if newCookingTime != 0:
                #       newStep.actionParameter = str(newCookingTime)+ " min"
                # change the equipment
                if newStep.equipmentArray != []:
                        newStep.equipmentArray = []
                if newStep.action == "Braise" or newStep.action == "Stew":
                        newStep.equipmentArray.append('Pot')
                if newStep.action == "Bake" or newStep.action == "Broil" or newStep.action == "Roast":
                        newStep.equipmentArray.append('Oven')
                if newStep.action == "Fry" or newStep.action == "Stir-fry":
                        newStep.equipmentArray.append('Pan')
                newStep.originalString = newStep.originalString.replace(oldStep.action.lower(), newCookingMethod.lower());
                newStep.originalString = newStep.originalString.replace(oldStep.action.title(), newCookingMethod.title());
                newStep.modified = True
                self.stepArray[oldStepIndex] = newStep
                
                # remove necessary preparing steps
                self.removePreStep(oldStep,newCookingMethod.title())
                # add necessary preparing steps
                self.addPreStep(newStep)
                
                self.title = self.title.replace(oldStep.action.title(), newCookingMethod.title())
                return str(newStep)
                
        def removePreStep(self, oldStep, newCookingMethod):
                if ((oldStep.action == "Braise" or oldStep.action == "Stew") 
                and newCookingMethod != "Braise" and newCookingMethod != "Stew"):
                        for step in self.stepArray:
                                if 'add' in step.action.lower() and step.target == oldStep.target:
                                        for ing in step.ingredientArray:
                                                if 'water' in ing.name.lower() or 'liquid' in ing.name.lower():
                                                        self.stepArray.remove(step)
                                                        break
                if ((newCookingMethod == "Braise" or newCookingMethod == "Stew") 
                and oldStep.action != "Braise" and oldStep.action != "Stew"):
                        for step in self.stepArray:
                                if ('chop' in step.action.lower() or 'cut' in step.action.lower()) and step.target == oldStep.target:
                                        self.stepArray.remove(step)
                                        
        def addPreStep(self, newStep):
                newStepIndex = self.stepArray.index(newStep)
                if newStep.action == 'Braise' or newStep.action == 'Stew':
                        PreStepExisted = False
                        for step in self.stepArray:
                                if 'add' in step.action.lower() and step.target == newStep.target:
                                        for ing in step.ingredientArray:
                                                if 'water' in ing.name.lower() or 'liquid' in ing.name.lower():
                                                        PreStepExisted = True
                                                        break
                        if PreStepExisted == False:
                                NewIng = Ingredient('water', 'sauce')
                                NewPreStep = Step("Add water", True, [NewIng], 'Add', 'NA', newStep.target, ['pot'], newStep.target)
                                self.stepArray.insert(newStepIndex,NewPreStep)
                                
                if 'fry' in newStep.action.lower() or newStep.action == 'Broil':
                        PreStepExisted = False
                        for step in self.stepArray:
                                if (('chop' in step.action.lower() or 'cut' in step.action.lower())
                                and step.target == newStep.target):
                                        PreStepExisted = True
                        """if PreStepExisted == False:
                                NewIng = Ingredient(newStep.target, 'protein')
                                desc = "Cut "+ newStep.target + " to small pieces"
                                NewPreStep = Step(desc, False, [NewIng], 'Cut', 'NA', newStep.target, [], newStep.target)
                                self.stepArray.insert(newStepIndex,NewPreStep)
                        """
class recipeParser:
    #This class is used to parse a recipe from an input file in to the 
    parsedRecipe = Recipe('', [], 0, 0, [])

    def __init__(self):
        return
    def getPreconditions(self, fileName):
        #open the file
        rFile = open(fileName, 'r')
        self.parsedRecipe.title = rFile.readline() #The first line of the file contains the title
        index = -1
        #read the file until you find the string 'Ready In:' the next line will contain time information
        while index == -1:
            line = rFile.readline()
            index = line.find("Ready In:")

        rFile.readline()
        line = rFile.readline()
        wordList = line.split(' ')

        self.parsedRecipe.timeToCook = int(wordList[0])

        #calculate time in minutes
        if wordList[1] == 'Hrs':
            self.parsedRecipe.timeToCook *= 60

        if len(wordList) > 2:
            self.parsedRecipe.timeToCook += int(wordList[2])
        
        #find serving information
        index = -1
        while index == -1:
            line = rFile.readline()
            index = line.find("Recipe Yield ")

        wordList = line.split(' ')

        self.parsedRecipe.yieldAmount = int(wordList[3])

        #parse ingredient list
        index = -1
        while index == -1:
            line = rFile.readline()
            index = line.find("Ingredients")

        index = -1
        while True:
            line = rFile.readline()
            line = line.lower()
            index = line.find("directions")
            if index != -1:
                break
            if line != '\n' and line != '\r\n':
                wordCounter = 0
                numWords = len(wordList)
                wordList = line.split(' ')
                quantity = 0
                units = ''
                name = ''
                type = ''
                #special case for eggs since they don't have units
                if wordList[1].find('egg') != -1 or wordList[1].find('eggs') != -1:
                    quantity = int(wordList[0])
                    units = ''
                    name = 'egg'
                    type = 'protein'
                    ingred = Ingredient(name, type, quantity, units)
                    self.parsedRecipe.ingredientArray.append(ingred)
                #get quantity
                else:
                    nextWord = wordList[wordCounter]
                    #cases for fractions
                    if (nextWord.find('/')) != -1:
                        quantity += float(nextWord[0])/float(nextWord[2])
                        wordCounter += 1
                        nextWord = wordList[wordCounter]
                    #normal case
                    else:
                        quantity = float(wordList[wordCounter])
                        wordCounter += 1
                        nextWord = wordList[wordCounter]
                    #more fraction casing
                        if nextWord.find('/') != -1:
                            quantity += float(nextWord[0])/float(nextWord[2])
                            wordCounter += 1
                            nextWord = wordList[wordCounter]
                    #cases where units need clarificatio, e.g. 1 (28 oz) can is turned in to 28 oz
                    if nextWord.find('(') != -1:
                        quantity *= float(nextWord.lstrip('('))
                        wordCounter += 1
                        nextWord = wordList[wordCounter]
                        units = nextWord.rstrip(')')
                        wordCounter += 2
                        nextWord = wordList[wordCounter]
                    else:
                        units = nextWord
                        wordCounter += 1
                        nextWord = wordList[wordCounter]
                    dict = Dictionary("dictionary.csv")
                    ingredList = dict.getIngredients()
                    name = ''
                    type = ''
                    twoWordIngredList = filter(twoWords, ingredList)
                    for ingredient in twoWordIngredList:
                        if line.find(str(ingredient).lower()) != -1:
                            name = str(ingredient);
                            type = ingredient.ingType
                            break
                    if name == '':
                        for ingredient in ingredList:
                            if line.find(str(ingredient).lower()) != -1:
                                name = str(ingredient);
                                type = ingredient.ingType
                                break
                    if name == '':
                       name = 'Ingredient Name Not Found!'
                       type = 'Ingredient Type Unknown!'
                    ingred = Ingredient(name, type, quantity, units)
                    self.parsedRecipe.ingredientArray.append(ingred)      
        rFile.close()
        return
    def getSteps(self, fileName):
        rFile = open(fileName, 'r')
        line = rFile.readline()
        dict = Dictionary("dictionary.csv")
        while line.find('Directions') == -1:
            line = rFile.readline()
        line = rFile.readline()
        while line != '':
            if line != '\n' and line != '\r\n':
                line = line.replace('-in.','inch')
                line = line.replace(' in.','inch')
                sentenceArray = line.split('.')
                for sentence in iter(sentenceArray):
                    sentenceCopy = copy.copy(sentence)
                    sentenceAction = ''
                    sentenceIngredList = []
                    sentenceEquipmentList = []
                    actionList = dict.getActions()
                    for action in actionList:
                        if sentence.find(str(action).lower()) != -1:
                            sentenceAction = str(action)
                            break
                    ingredList = dict.getIngredients()
                    for ingred in ingredList:
                        if sentence.find(str(ingred).lower()) != -1:
                            sentence = sentence.replace(str(ingred).lower(), '')
                            sentenceIngredList.append(ingred)
                    equipmentList = dict.getEquipment()
                    for equip in equipmentList:
                        if sentence.find(str(equip).lower()) != -1:
                            sentenceEquipmentList.append(equip)
                    newStep = Step(sentenceCopy, False, sentenceIngredList, sentenceAction, "No Parameters Set", "No Target Ingredient Set", sentenceEquipmentList, "No Products Found")
                    self.parsedRecipe.stepArray.append(newStep)
                line = rFile.readline()
            else:
                line = rFile.readline()
        rFile.close()
        return
    def parseRecipe(self, fileName):
        self.getPreconditions(fileName)
        self.getSteps(fileName)
        return self.parsedRecipe

                        
#converts units into mls - assumes 1 floz = 1 oz
def normalizeQuantity(ingredient):
            quantity = int(ingredient.amount)
            if ingredient.unit in ('tsp','teaspoon','teaspoons'):
                quantity *= 5
            elif ingredient.unit in ('tbsp','tablespoon','tablespoons'):
                quantity *= 15
            elif ingredient.unit in ('cup','cups'):
                quantity *= 240
            elif ingredient.unit in ('oz','ozs','ounce','ounces'):
                quantity *= 30
            elif ingredient.unit in ('lb','lbs','pound','pounds'):
                quantity *= 480
            elif ingredient.unit in ('gallon','gallons'):
                quantity *= 3785
            elif ingredient.unit in ('quart','quarts'):
                quantity *= 946
            elif ingredient.unit in ('pint','pints'):
                quantity *= 473
            return quantity

def twoWords(ingredient):
        return str(ingredient).find(' ') != -1
